/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

/* global cordova, Windows, Media, MediaError, LocalFileSystem, halfSpeedBtn */

// increased timeout for actual playback to give device chance to download and play mp3 file
// some emulators can be REALLY slow at this, so two minutes
var ACTUAL_PLAYBACK_TEST_TIMEOUT = 2 * 60 * 1000;

var WEB_MP3_FILE = 'https://cordova.apache.org/static/downloads/BlueZedEx.mp3';
var WEB_MP3_STREAM = 'https://cordova.apache.org/static/downloads/BlueZedEx.mp3';

var isWindows = cordova.platformId === 'windows8' || cordova.platformId === 'windows';
var isBrowser = cordova.platformId === 'browser';
// Detect whether audio hardware is available and enabled. For iOS playing audio is
// not supported on emulators w/out sound device connected to host PC but (which is
// the case for Sauce Labs emulators - see CB-11430)
var isAudioSupported = isWindows
    ? !!Windows.Media.Devices.MediaDevice.getDefaultAudioRenderId(Windows.Media.Devices.AudioDeviceRole.default)
    : cordova.platformId === 'ios'
        ? !window.SAUCELABS_ENV
        : true;

var isKitKat = cordova.platformId === 'android' && /Android\s4\.4/.test(window.navigator.userAgent);

exports.defineAutoTests = function () {
    var failed = function (done, msg, context) {
        if (context && context.done) return;
        context.done = true;
        var info = typeof msg === 'undefined' ? 'Unexpected error callback' : msg;
        expect(true).toFailWithMessage(info);
        done();
    };

    var succeed = function (done, msg, context) {
        if (context && context.done) return;
        context.done = true;
        var info = typeof msg === 'undefined' ? 'Unexpected success callback' : msg;
        expect(true).toFailWithMessage(info);
        done();
    };

    describe('Media', function () {
        beforeEach(function () {
            // Custom Matcher
            jasmine.Expectation.addMatchers({
                toFailWithMessage: function () {
                    return {
                        compare: function (_, message) {
                            var pass = false;
                            return {
                                pass: pass,
                                message: message
                            };
                        }
                    };
                }
            });
        });

        it('media.spec.1 should exist', function () {
            expect(Media).toBeDefined();
            expect(typeof Media).toBe('function');
        });

        it('media.spec.2 should have the following properties', function () {
            var media1 = new Media('dummy');
            expect(media1.id).toBeDefined();
            expect(media1.src).toBeDefined();
            expect(media1._duration).toBeDefined();
            expect(media1._position).toBeDefined();
            media1.release();
        });

        it('media.spec.3 should define constants for Media status', function () {
            expect(Media).toBeDefined();
            expect(Media.MEDIA_NONE).toBe(0);
            expect(Media.MEDIA_STARTING).toBe(1);
            expect(Media.MEDIA_RUNNING).toBe(2);
            expect(Media.MEDIA_PAUSED).toBe(3);
            expect(Media.MEDIA_STOPPED).toBe(4);
        });

        it('media.spec.4 should define constants for Media errors', function () {
            expect(MediaError).toBeDefined();
            expect(MediaError.MEDIA_ERR_NONE_ACTIVE).toBe(0);
            expect(MediaError.MEDIA_ERR_ABORTED).toBe(1);
            expect(MediaError.MEDIA_ERR_NETWORK).toBe(2);
            expect(MediaError.MEDIA_ERR_DECODE).toBe(3);
            expect(MediaError.MEDIA_ERR_NONE_SUPPORTED).toBe(4);
        });

        it('media.spec.5 should contain a play function', function () {
            var media1 = new Media('dummy');
            expect(media1.play).toBeDefined();
            expect(typeof media1.play).toBe('function');
            media1.release();
        });

        it('media.spec.6 should contain a stop function', function () {
            var media1 = new Media('dummy');
            expect(media1.stop).toBeDefined();
            expect(typeof media1.stop).toBe('function');
            media1.release();
        });

        it('media.spec.7 should contain a seekTo function', function () {
            var media1 = new Media('dummy');
            expect(media1.seekTo).toBeDefined();
            expect(typeof media1.seekTo).toBe('function');
            media1.release();
        });

        it('media.spec.8 should contain a pause function', function () {
            var media1 = new Media('dummy');
            expect(media1.pause).toBeDefined();
            expect(typeof media1.pause).toBe('function');
            media1.release();
        });

        it('media.spec.9 should contain a getDuration function', function () {
            var media1 = new Media('dummy');
            expect(media1.getDuration).toBeDefined();
            expect(typeof media1.getDuration).toBe('function');
            media1.release();
        });

        it('media.spec.10 should contain a getCurrentPosition function', function () {
            var media1 = new Media('dummy');
            expect(media1.getCurrentPosition).toBeDefined();
            expect(typeof media1.getCurrentPosition).toBe('function');
            media1.release();
        });

        it('media.spec.11 should contain a startRecord function', function () {
            var media1 = new Media('dummy');
            expect(media1.startRecord).toBeDefined();
            expect(typeof media1.startRecord).toBe('function');
            media1.release();
        });

        it('media.spec.12 should contain a stopRecord function', function () {
            var media1 = new Media('dummy');
            expect(media1.stopRecord).toBeDefined();
            expect(typeof media1.stopRecord).toBe('function');
            media1.release();
        });

        it('media.spec.13 should contain a release function', function () {
            var media1 = new Media('dummy');
            expect(media1.release).toBeDefined();
            expect(typeof media1.release).toBe('function');
            media1.release();
        });

        it('media.spec.14 should contain a setVolume function', function () {
            var media1 = new Media('dummy');
            expect(media1.setVolume).toBeDefined();
            expect(typeof media1.setVolume).toBe('function');
            media1.release();
        });

        it('media.spec.15 should contain a getCurrentAmplitude function', function () {
            var media1 = new Media('dummy');
            expect(media1.getCurrentAmplitude).toBeDefined();
            expect(typeof media1.getCurrentAmplitude).toBe('function');
            media1.release();
        });

        it('media.spec.16 should contain a pauseRecord function', function () {
            var media1 = new Media('dummy');
            expect(media1.pauseRecord).toBeDefined();
            expect(typeof media1.pauseRecord).toBe('function');
            media1.release();
        });

        it('media.spec.17 should contain a resumeRecord function', function () {
            var media1 = new Media('dummy');
            expect(media1.resumeRecord).toBeDefined();
            expect(typeof media1.resumeRecord).toBe('function');
            media1.release();
        });

        it('media.spec.18 should return MediaError for bad filename', function (done) {
            // bb10 dialog pops up, preventing tests from running
            if (cordova.platformId === 'blackberry10') {
                pending();
            }

            var context = this;
            var fileName = 'invalid.file.name';
            var badMedia = new Media(
                fileName,
                succeed.bind(
                    null,
                    done,
                    ' badMedia = new Media , Unexpected succees callback, it should not create Media object with invalid file name'
                ),
                function (result) {
                    if (context.done) return;
                    context.done = true;

                    expect(result).toBeDefined();
                    expect(result.code).toBe(MediaError.MEDIA_ERR_ABORTED);
                    if (badMedia) {
                        badMedia.release();
                    }
                    done();
                }
            );
            badMedia.play();
        });

        describe('actual playback', function () {
            var checkInterval, media;

            afterEach(function () {
                clearInterval(checkInterval);
                if (media) {
                    media.stop();
                    media.release();
                    media = null;
                }
            });

            it(
                'media.spec.19 position should be set properly',
                function (done) {
                    // no audio hardware available
                    if (!isAudioSupported || isBrowser || isKitKat) {
                        pending();
                    }

                    // context variable used as an extra security statement to ensure that the callback is processed only once,
                    // in case the statusChange callback is reached more than one time with the same status code.
                    // Some information about this kind of behaviour can be found at JIRA: CB-7099.
                    var context = this;
                    var mediaFile = WEB_MP3_FILE;
                    var successCallback = function () {};
                    var statusChange = function (statusCode) {
                        if (!context.done && statusCode === Media.MEDIA_RUNNING) {
                            checkInterval = setInterval(function () {
                                if (context.done) return;
                                media.getCurrentPosition(function successCallback (position) {
                                    if (position > 0.0) {
                                        context.done = true;
                                        expect(true).toBe(true);
                                        done();
                                    }
                                }, failed.bind(null, done, 'media1.getCurrentPosition - Error getting media current position', context));
                            }, 1000);
                        }
                    };
                    media = new Media(
                        mediaFile,
                        successCallback,
                        failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context),
                        statusChange
                    );
                    media.play();
                },
                ACTUAL_PLAYBACK_TEST_TIMEOUT
            );

            it(
                'media.spec.20 duration should be set properly',
                function (done) {
                    if (!isAudioSupported || cordova.platformId === 'blackberry10' || isBrowser || isKitKat) {
                        pending();
                    }

                    // context variable used as an extra security statement to ensure that the callback is processed only once,
                    // in case the statusChange callback is reached more than one time with the same status code.
                    // Some information about this kind of behaviour can be found at JIRA: CB-7099.
                    var context = this;
                    var mediaFile = WEB_MP3_FILE;
                    var successCallback = function () {};
                    var statusChange = function (statusCode) {
                        if (!context.done && statusCode === Media.MEDIA_RUNNING) {
                            checkInterval = setInterval(function () {
                                if (context.done) return;
                                media.getCurrentPosition(function (position) {
                                    if (position > 0.0) {
                                        context.done = true;
                                        expect(media.getDuration()).toBeGreaterThan(0.0);
                                        done();
                                    }
                                }, failed.bind(null, done, 'media1.getCurrentPosition - Error getting media current position', context));
                            }, 1000);
                        }
                    };
                    media = new Media(
                        mediaFile,
                        successCallback,
                        failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context),
                        statusChange
                    );
                    media.play();
                },
                ACTUAL_PLAYBACK_TEST_TIMEOUT
            );

            it(
                'media.spec.21 should be able to resume playback after pause',
                function (done) {
                    if (!isAudioSupported || cordova.platformId === 'blackberry10' || isKitKat) {
                        pending();
                    }

                    // context variable used as an extra security statement to ensure that the callback is processed only once,
                    // in case the statusChange callback is reached more than one time with the same status code.
                    // Some information about this kind of behaviour can be found at JIRA: CB-7099.
                    var context = this;
                    var resumed = false;
                    var mediaFile = WEB_MP3_FILE;
                    var successCallback = function () {};
                    var statusChange = function (statusCode) {
                        if (context.done) return;

                        if (statusCode === Media.MEDIA_RUNNING) {
                            if (!resumed) {
                                media.seekTo(20000);
                                media.pause();
                                return;
                            }

                            media.getCurrentPosition(function (position) {
                                expect(position).toBeGreaterThan(19);
                                expect(position).toBeLessThan(21);
                                context.done = true;
                                done();
                            }, failed.bind(null, done, 'media1.getCurrentPosition - Error getting media current position', context));
                        }

                        if (statusCode === Media.MEDIA_PAUSED) {
                            resumed = true;
                            media.play();
                        }
                    };
                    media = new Media(
                        mediaFile,
                        successCallback,
                        failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context),
                        statusChange
                    );

                    // CB-10535: Play after a few secs, to give allow enough buffering of media file before seeking
                    setTimeout(function () {
                        media.play();
                    }, 4000);
                },
                ACTUAL_PLAYBACK_TEST_TIMEOUT
            );

            it(
                'media.spec.22 should be able to seek through file',
                function (done) {
                    if (!isAudioSupported || cordova.platformId === 'blackberry10' || isKitKat) {
                        pending();
                    }

                    // context variable used as an extra security statement to ensure that the callback is processed only once,
                    // in case the statusChange callback is reached more than one time with the same status code.
                    // Some information about this kind of behaviour can be found at JIRA: CB-7099.
                    var context = this;
                    var mediaFile = WEB_MP3_FILE;
                    var successCallback = function () {};
                    var statusChange = function (statusCode) {
                        if (!context.done && statusCode === Media.MEDIA_RUNNING) {
                            checkInterval = setInterval(function () {
                                if (context.done) return;
                                media.seekTo(5000);
                                media.getCurrentPosition(function (position) {
                                    expect(position).toBeCloseTo(5, 0);
                                    context.done = true;
                                    done();
                                }, failed.bind(null, done, 'media1.getCurrentPosition - Error getting media current position', context));
                            }, 1000);
                        }
                    };
                    media = new Media(
                        mediaFile,
                        successCallback,
                        failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context),
                        statusChange
                    );

                    // CB-10535: Play after a few secs, to give allow enough buffering of media file before seeking
                    setTimeout(function () {
                        media.play();
                    }, 4000);
                },
                ACTUAL_PLAYBACK_TEST_TIMEOUT
            );
        });

        it('media.spec.23 should contain a setRate function', function () {
            var media1 = new Media('dummy');
            expect(media1.setRate).toBeDefined();
            expect(typeof media1.setRate).toBe('function');
            media1.release();
        });

        it(
            'media.spec.24 playback rate should be set properly using setRate',
            function (done) {
                if (cordova.platformId !== 'ios') {
                    expect(true).toFailWithMessage('Platform does not supported this feature');
                    pending();
                }

                // no audio hardware available
                if (!isAudioSupported) {
                    pending();
                }

                var mediaFile = WEB_MP3_FILE;
                var successCallback;
                var context = this;
                var flag = true;
                var statusChange = function (statusCode) {
                    console.log('status code: ' + statusCode);
                    if (statusCode === Media.MEDIA_RUNNING && flag) {
                        // flag variable used to ensure an extra security statement to ensure that the callback is processed only once,
                        // in case for some reason the statusChange callback is reached more than one time with the same status code.
                        // Some information about this kind of behavior it can be found at JIRA: CB-7099
                        flag = false;
                        setTimeout(function () {
                            media1.getCurrentPosition(
                                function (position) {
                                    // in four seconds expect position to be between 4 & 10. Here, the values are chosen to give
                                    // a large enough buffer range for the position to fall in and are not based on any calculation.
                                    expect(position).not.toBeLessThan(4);
                                    expect(position).toBeLessThan(10);
                                    media1.stop();
                                    media1.release();
                                    context.done = true;
                                    done();
                                },
                                failed.bind(null, done, 'media1.getCurrentPosition - Error getting media current position'),
                                context
                            );
                        }, 4000);
                    }
                };

                var media1 = new Media(
                    mediaFile,
                    successCallback,
                    failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context),
                    statusChange
                );
                // make audio playback two times faster
                media1.setRate(2);
                media1.play();
            },
            ACTUAL_PLAYBACK_TEST_TIMEOUT
        );

        it(
            'media.spec.25 should be able to play an audio stream',
            function (done) {
                // no audio hardware available, OR
                // O_o Safari can't play the stream, so we're skipping this test on all browsers o_O
                if (!isAudioSupported || isBrowser || isKitKat) {
                    pending();
                }

                // The link below points to an infinite mp3 stream
                var mediaFile = WEB_MP3_STREAM;
                var successCallback;
                var context = this;
                var flag = true;
                var statusChange = function (statusCode) {
                    console.log('status code: ' + statusCode);
                    if (statusCode === Media.MEDIA_RUNNING && flag) {
                        // flag variable used to ensure an extra security statement to ensure that the callback is processed only once,
                        // in case for some reason the statusChange callback is reached more than one time with the same status code.
                        // Some information about this kind of behavior it can be found at JIRA: CB-7099
                        flag = false;
                        expect(true).toBe(true);
                        media1.stop();
                        media1.release();
                        context.done = true;
                        done();
                    }
                };

                var media1 = new Media(
                    mediaFile,
                    successCallback,
                    failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context),
                    statusChange
                );
                media1.play();
            },
            ACTUAL_PLAYBACK_TEST_TIMEOUT
        );

        it('media.spec.26 should not crash or throw when setting the volume right after creating the media', function (done) {
            // bb10 dialog pops up, preventing tests from running
            if (cordova.platformId === 'blackberry10') {
                pending();
            }

            var mediaFile = WEB_MP3_FILE;
            var media = null;

            expect(function () {
                media = new Media(mediaFile);
                media.setVolume('0.5');
            }).not.toThrow();

            // if there is no exception or crash in 3 seconds, the spec is completed
            setTimeout(function () {
                if (media) {
                    media.release();
                    done();
                }
            }, 3000);
        });

        it('media.spec.27 should call success or error when trying to stop a media that is in starting state', function (done) {
            // bb10 dialog pops up, preventing tests from running
            if (!isAudioSupported || cordova.platformId === 'blackberry10') {
                pending();
            }

            var mediaFile = WEB_MP3_FILE;
            var media = null;
            var context = this;
            var beenStarting = false;
            var safeDone = function () {
                if (!context.done) {
                    media.release();
                    context.done = true;
                    done();
                }
            };

            var errorCallback = jasmine.createSpy('errorCallback').and.callFake(function (e) {
                expect(true).toBe(true);
                safeDone();
            });
            var successCallback = function () {
                expect(true).toBe(true);
                safeDone();
            };
            var statusChange = function (s) {
                if (s === Media.MEDIA_STARTING && !context.done) {
                    beenStarting = true;
                    media.stop();
                } else if (s === Media.MEDIA_RUNNING) {
                    // Some plugin implementations may skip "Starting" state
                    // so we'll also try to call stop in "Running" state,
                    // but in this case we should check that the "Starting" state wasn't really reached,
                    // otherwise it would mean that the previous media.stop() call has been ignored
                    expect(beenStarting).toBe(false);
                    media.stop();
                }
            };

            media = new Media(mediaFile, successCallback, errorCallback, statusChange);
            media.play();
        });
    });
};

//* *****************************************************************************************
//* **************************************Manual Tests***************************************
//* *****************************************************************************************

exports.defineManualTests = function (contentEl, createActionButton) {
    // -------------------------------------------------------------------------
    // Audio player
    // -------------------------------------------------------------------------
    var media1 = null;
    var media1Timer = null;
    var audioSrc = null;
    var defaultaudio = WEB_MP3_FILE;

    // Play audio function
    function playAudio (url) {
        console.log('playAudio()');
        console.log(' -- media=' + media1);

        var src = defaultaudio;

        if (url) {
            src = url;
        }

        // Stop playing if src is different from currently playing source
        if (src !== audioSrc) {
            if (media1 !== null) {
                stopAudio();
                media1 = null;
            }
        }

        if (media1 === null) {
            // TEST STREAMING AUDIO PLAYBACK
            // var src = "http://nunzioweb.com/misc/Bon_Jovi-Crush_Mystery_Train.mp3";   // works
            // var src = "http://nunzioweb.com/misc/Bon_Jovi-Crush_Mystery_Train.m3u"; // doesn't work
            // var src = "http://www.wav-sounds.com/cartoon/bugsbunny1.wav"; // works
            // var src = "http://www.angelfire.com/fl5/html-tutorial/a/couldyou.mid"; // doesn't work
            // var src = "MusicSearch/mp3/train.mp3";    // works
            // var src = "bryce.mp3";  // works
            // var src = "/android_asset/www/bryce.mp3"; // works

            media1 = new Media(
                src,
                function () {
                    console.log('playAudio():Audio Success');
                },
                function (err) {
                    console.log('playAudio():Audio Error: ' + err.code);
                    setAudioStatus('Error: ' + err.code);
                },
                function (status) {
                    console.log('playAudio():Audio Status: ' + status);
                    setAudioStatus(Media.MEDIA_MSG[status]);

                    // If stopped, then stop getting current position
                    if (Media.MEDIA_STOPPED === status) {
                        clearInterval(media1Timer);
                        media1Timer = null;
                        setAudioPosition('0 sec');
                    }
                }
            );
        }
        audioSrc = src;
        document.getElementById('durationValue').innerHTML = '';
        // Play audio
        media1.play();
        if (media1Timer === null && media1.getCurrentPosition) {
            media1Timer = setInterval(function () {
                media1.getCurrentPosition(
                    function (position) {
                        if (position >= 0.0) {
                            setAudioPosition(position + ' sec');
                        }
                    },
                    function (e) {
                        console.log('Error getting pos=' + e);
                        setAudioPosition('Error: ' + e);
                    }
                );
            }, 1000);
        }

        // Get duration
        var counter = 0;
        var timerDur = setInterval(function () {
            counter = counter + 100;
            if (counter > 2000) {
                clearInterval(timerDur);
            }
            var dur = media1.getDuration();
            if (dur > 0) {
                clearInterval(timerDur);
                document.getElementById('durationValue').innerHTML = dur + ' sec';
            }
        }, 100);
    }

    // Pause audio playback
    function pauseAudio () {
        console.log('pauseAudio()');
        if (media1) {
            media1.pause();
        }
    }

    // Stop audio
    function stopAudio () {
        console.log('stopAudio()');
        if (media1) {
            media1.stop();
        }
        clearInterval(media1Timer);
        media1Timer = null;
    }

    // Release audio
    function releaseAudio () {
        console.log('releaseAudio()');
        if (media1) {
            media1.stop(); // imlied stop of playback, resets timer
            media1.release();
        }
    }

    // Set audio status
    function setAudioStatus (status) {
        document.getElementById('statusValue').innerHTML = status;
    }

    // Set audio position
    function setAudioPosition (position) {
        document.getElementById('positionValue').innerHTML = position;
    }

    // Seek audio
    function seekAudio (mode) {
        var time = document.getElementById('seekInput').value;
        if (time === '') {
            time = 5000;
        } else {
            time = time * 1000; // we expect the input to be in seconds
        }
        if (media1 === null) {
            console.log('seekTo requested while media1 is null');
            if (audioSrc === null) {
                audioSrc = defaultaudio;
            }
            media1 = new Media(
                audioSrc,
                function () {
                    console.log('seekToAudio():Audio Success');
                },
                function (err) {
                    console.log('seekAudio():Audio Error: ' + err.code);
                    setAudioStatus('Error: ' + err.code);
                },
                function (status) {
                    console.log('seekAudio():Audio Status: ' + status);
                    setAudioStatus(Media.MEDIA_MSG[status]);

                    // If stopped, then stop getting current position
                    if (Media.MEDIA_STOPPED === status) {
                        clearInterval(media1Timer);
                        media1Timer = null;
                        setAudioPosition('0 sec');
                    }
                }
            );
        }

        media1.getCurrentPosition(
            function (position) {
                var deltat = time;
                if (mode === 'by') {
                    deltat = time + position * 1000;
                }
                media1.seekTo(
                    deltat,
                    function () {
                        console.log('seekAudioTo():Audio Success');
                        // force an update on the position display
                        updatePosition();
                    },
                    function (err) {
                        console.log('seekAudioTo():Audio Error: ' + err.code);
                    }
                );
            },
            function (e) {
                console.log('Error getting pos=' + e);
                setAudioPosition('Error: ' + e);
            }
        );
    }

    // set audio volume
    function setVolume () {
        console.log('setVolume()');
        var volume = document.getElementById('volumeInput').value;
        if (media1 !== null) {
            media1.setVolume(volume);
        }
    }

    // for forced updates of position after a successful seek

    function updatePosition () {
        media1.getCurrentPosition(
            function (position) {
                if (position >= 0.0) {
                    setAudioPosition(position + ' sec');
                }
            },
            function (e) {
                console.log('Error getting pos=' + e);
                setAudioPosition('Error: ' + e);
            }
        );
    }

    // -------------------------------------------------------------------------
    // Audio recorder
    // -------------------------------------------------------------------------
    var mediaRec = null;
    var recTime = 0;
    var recordSrc = 'myRecording.mp3';

    // Record audio
    function recordAudio () {
        console.log('recordAudio(), recording to ' + recordSrc);
        console.log(' -- media=' + mediaRec);

        releaseAudio();

        if (!mediaRec) {
            mediaRec = new Media(
                recordSrc,
                function () {
                    console.log('recordAudio():Audio Success');
                },
                function (err) {
                    console.log('recordAudio():Audio Error: ' + err.code);
                    setAudioStatus('Error: ' + err.code);
                },
                function (status) {
                    console.log('recordAudio():Audio Status: ' + status);
                    setAudioStatus(Media.MEDIA_MSG[status]);
                }
            );
        }

        // Record audio
        mediaRec.startRecord();

        // Stop recording after 10 sec
        recTime = 0;
        var recInterval = setInterval(function () {
            recTime = recTime + 1;
            setAudioPosition(recTime + ' sec');
            if (recTime >= 10) {
                clearInterval(recInterval);
                if (mediaRec.stopAudioRecord) {
                    mediaRec.stopAudioRecord();
                } else {
                    mediaRec.stopRecord();
                }
                console.log('recordAudio(): stop');
            }
        }, 1000);
    }

    // Play back recorded audio
    function playRecording () {
        playAudio(recordSrc);
    }

    // Function to get a filename for iOS recording
    // Ensures that file doesn't exist to test CB-11380
    function getRecordSrc () {
        var noop = function () {};
        recordSrc = 'cdvfile://localhost/temporary/iOSRecording.wav';
        window.resolveLocalFileSystemURL(
            recordSrc,
            function (file) {
                file.remove(function () {
                    console.log('Successfully removed ' + recordSrc);
                }, noop);
            },
            noop
        );
    }

    // Function to create a file for BB recording
    function getRecordSrcBB () {
        var fsFail = function (error) {
            console.log('error creating file for BB recording', error);
        };
        var gotFile = function (file) {
            recordSrc = file.fullPath;
        };
        var gotFS = function (fileSystem) {
            fileSystem.root.getFile(
                'BBRecording.amr',
                {
                    create: true
                },
                gotFile,
                fsFail
            );
        };
        window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, gotFS, fsFail);
    }

    // Function to create a file for Windows recording
    function getRecordSrcWin () {
        var fsFail = function (error) {
            console.log('error creating file for Win recording', error);
        };
        var gotFile = function (file) {
            recordSrc = file.name;
        };
        var gotFS = function (fileSystem) {
            fileSystem.root.getFile(
                'WinRecording.m4a',
                {
                    create: true
                },
                gotFile,
                fsFail
            );
        };
        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fsFail);
    }

    // Generate Dynamic Table
    function generateTable (tableId, rows, cells, elements) {
        var table = document.createElement('table');
        for (var r = 0; r < rows; r++) {
            var row = table.insertRow(r);
            for (var c = 0; c < cells; c++) {
                var cell = row.insertCell(c);
                cell.setAttribute('align', 'center');
                for (var e in elements) {
                    if (elements[e].position.row === r && elements[e].position.cell === c) {
                        var htmlElement = document.createElement(elements[e].tag);
                        var content;

                        if (elements[e].content !== '') {
                            content = document.createTextNode(elements[e].content);
                            htmlElement.appendChild(content);
                        }
                        if (elements[e].type) {
                            htmlElement.type = elements[e].type;
                        }
                        htmlElement.setAttribute('id', elements[e].id);
                        cell.appendChild(htmlElement);
                    }
                }
            }
        }
        table.setAttribute('align', 'center');
        table.setAttribute('id', tableId);
        return table;
    }

    // Audio && Record Elements
    var elementsResultsAudio = [
        {
            id: 'statusTag',
            content: 'Status:',
            tag: 'div',
            position: {
                row: 0,
                cell: 0
            }
        },
        {
            id: 'statusValue',
            content: '',
            tag: 'div',
            position: {
                row: 0,
                cell: 2
            }
        },
        {
            id: 'durationTag',
            content: 'Duration:',
            tag: 'div',
            position: {
                row: 1,
                cell: 0
            }
        },
        {
            id: 'durationValue',
            content: '',
            tag: 'div',
            position: {
                row: 1,
                cell: 2
            }
        },
        {
            id: 'positionTag',
            content: 'Position:',
            tag: 'div',
            position: {
                row: 2,
                cell: 0
            }
        },
        {
            id: 'positionValue',
            content: '',
            tag: 'div',
            position: {
                row: 2,
                cell: 2
            }
        }
    ];
    var elementsAudio = [
        {
            id: 'playBtn',
            content: '',
            tag: 'div',
            position: {
                row: 0,
                cell: 0
            }
        },
        {
            id: 'pauseBtn',
            content: '',
            tag: 'div',
            position: {
                row: 0,
                cell: 1
            }
        },
        {
            id: 'stopBtn',
            content: '',
            tag: 'div',
            position: {
                row: 1,
                cell: 0
            }
        },
        {
            id: 'releaseBtn',
            content: '',
            tag: 'div',
            position: {
                row: 1,
                cell: 1
            }
        },
        {
            id: 'seekByBtn',
            content: '',
            tag: 'div',
            position: {
                row: 2,
                cell: 0
            }
        },
        {
            id: 'seekToBtn',
            content: '',
            tag: 'div',
            position: {
                row: 2,
                cell: 1
            }
        },
        {
            id: 'seekInput',
            content: '',
            tag: 'input',
            type: 'number',
            position: {
                row: 2,
                cell: 2
            }
        },
        {
            id: 'halfSpeedBtn',
            content: '',
            tag: 'div',
            position: {
                row: 0,
                cell: 2
            }
        },
        {
            id: 'setVolumeBtn',
            content: '',
            tag: 'div',
            position: {
                row: 3,
                cell: 0
            }
        },
        {
            id: 'volumeInput',
            tag: 'input',
            type: 'text',
            position: {
                row: 3,
                cell: 1
            }
        }
    ];
    var elementsRecord = [
        {
            id: 'recordbtn',
            content: '',
            tag: 'div',
            position: {
                row: 1,
                cell: 0
            }
        },
        {
            id: 'recordplayBtn',
            content: '',
            tag: 'div',
            position: {
                row: 1,
                cell: 1
            }
        },
        {
            id: 'recordpauseBtn',
            content: '',
            tag: 'div',
            position: {
                row: 2,
                cell: 0
            }
        },
        {
            id: 'recordstopBtn',
            content: '',
            tag: 'div',
            position: {
                row: 2,
                cell: 1
            }
        }
    ];

    // Title audio results
    var div = document.createElement('h2');
    div.appendChild(document.createTextNode('Audio'));
    div.setAttribute('align', 'center');
    contentEl.appendChild(div);
    // Generate and add results table
    contentEl.appendChild(generateTable('info', 3, 3, elementsResultsAudio));

    // Title audio actions
    div = document.createElement('h2');
    div.appendChild(document.createTextNode('Actions'));
    div.setAttribute('align', 'center');
    contentEl.appendChild(div);
    // Generate and add buttons table
    contentEl.appendChild(generateTable('audioActions', 4, 3, elementsAudio));
    createActionButton(
        'Play',
        function () {
            playAudio();
        },
        'playBtn'
    );
    createActionButton(
        'Pause',
        function () {
            pauseAudio();
        },
        'pauseBtn'
    );
    createActionButton(
        'HalfSpeed',
        function () {
            if (halfSpeedBtn.firstChild.firstChild.innerText === 'HalfSpeed') {
                halfSpeedBtn.firstChild.firstChild.innerText = 'FullSpeed';
                media1.setRate(0.5);
            } else if (halfSpeedBtn.firstChild.firstChild.innerText === 'FullSpeed') {
                halfSpeedBtn.firstChild.firstChild.innerText = 'DoubleSpeed';
                media1.setRate(1.0);
            } else {
                halfSpeedBtn.firstChild.firstChild.innerText = 'HalfSpeed';
                media1.setRate(2.0);
            }
        },
        'halfSpeedBtn'
    );
    createActionButton(
        'Stop',
        function () {
            stopAudio();
        },
        'stopBtn'
    );
    createActionButton(
        'Release',
        function () {
            releaseAudio();
        },
        'releaseBtn'
    );
    createActionButton(
        'Seek By',
        function () {
            seekAudio('by');
        },
        'seekByBtn'
    );
    createActionButton(
        'Seek To',
        function () {
            seekAudio('to');
        },
        'seekToBtn'
    );
    createActionButton(
        'Set volume',
        function () {
            setVolume();
        },
        'setVolumeBtn'
    );
    // get Special path to record if iOS || Blackberry
    if (cordova.platformId === 'ios') {
        getRecordSrc();
    } else if (cordova.platformId === 'blackberry') {
        getRecordSrcBB();
    } else if (cordova.platformId === 'windows' || cordova.platformId === 'windows8') {
        getRecordSrcWin();
    }

    // testing process and details
    function addItemToList (_list, _text) {
        var item = document.createElement('li');
        item.appendChild(document.createTextNode(_text));
        _list.appendChild(item);
    }

    div = document.createElement('h4');
    div.appendChild(document.createTextNode('Recommended Test Procedure'));
    contentEl.appendChild(div);

    var list = document.createElement('ol');
    addItemToList(
        list,
        'Press play - Will start playing audio. Status: Running, Duration: 61.333 sec, Position: Current position of audio track'
    );
    addItemToList(
        list,
        'Press pause - Will pause the audio. Status: Paused, Duration: 61.333 sec, Position: Position where track was paused'
    );
    addItemToList(list, 'Press play - Will begin playing where track left off from the pause');
    addItemToList(list, 'Press stop - Will stop the audio. Status: Stopped, Duration: 61.333 sec, Position: 0 sec');
    addItemToList(list, 'Press play - Will begin playing the audio track from the beginning');
    addItemToList(list, 'Press release - Will stop the audio. Status: Stopped, Duration: 61.333 sec, Position: 0 sec');
    addItemToList(list, 'Press play - Will begin playing the audio track from the beginning');
    addItemToList(list, 'Type 10 in the text box beside Seek To button');
    addItemToList(list, 'Press seek by - Will jump 10 seconds ahead in the audio track. Position: should jump by 10 sec');
    addItemToList(list, 'Press stop if track is not already stopped');
    addItemToList(list, 'Type 30 in the text box beside Seek To button');
    addItemToList(list, 'Press play then seek to - Should jump to Position 30 sec');
    addItemToList(list, 'Press stop');
    addItemToList(list, 'Type 5 in the text box beside Seek To button');
    addItemToList(list, 'Press play, let play past 10 seconds then press seek to - should jump back to position 5 sec');

    div = document.createElement('div');
    div.setAttribute(
        'style',
        'background:#B0C4DE;border:1px solid #FFA07A;margin:15px 6px 0px;min-width:295px;max-width:97%;padding:4px 0px 2px 10px;min-height:160px;max-height:200px;overflow:auto'
    );
    div.appendChild(list);
    contentEl.appendChild(div);

    // Title Record Audio
    div = document.createElement('h2');
    div.appendChild(document.createTextNode('Record Audio'));
    div.setAttribute('align', 'center');
    contentEl.appendChild(div);
    // Generate and add Record buttons table
    contentEl.appendChild(generateTable('recordContent', 3, 3, elementsRecord));
    createActionButton(
        'Record Audio \n 10 sec',
        function () {
            recordAudio();
        },
        'recordbtn'
    );
    createActionButton(
        'Play',
        function () {
            playRecording();
        },
        'recordplayBtn'
    );
    createActionButton(
        'Pause',
        function () {
            pauseAudio();
        },
        'recordpauseBtn'
    );
    createActionButton(
        'Stop',
        function () {
            stopAudio();
        },
        'recordstopBtn'
    );

    // testing process and details
    div = document.createElement('h4');
    div.appendChild(document.createTextNode('Recommended Test Procedure'));
    contentEl.appendChild(div);

    list = document.createElement('ol');
    addItemToList(
        list,
        'Press Record Audio 10 sec - Will start recording audio for 10 seconds. Status: Running, Position: number of seconds recorded (will stop at 10)'
    );
    addItemToList(list, 'Status will change to Stopped when finished recording');
    addItemToList(
        list,
        'Press play - Will begin playing the recording. Status: Running, Duration: # of seconds of recording, Position: Current position of recording'
    );
    addItemToList(
        list,
        'Press stop - Will stop playing the recording. Status: Stopped, Duration: # of seconds of recording, Position: 0 sec'
    );
    addItemToList(list, 'Press play - Will begin playing the recording from the beginning');
    addItemToList(
        list,
        'Press pause - Will pause the playback of the recording. Status: Paused, Duration: # of seconds of recording, Position: Position where recording was paused'
    );
    addItemToList(list, 'Press play - Will begin playing the recording from where it was paused');

    div = document.createElement('div');
    div.setAttribute(
        'style',
        'background:#B0C4DE;border:1px solid #FFA07A;margin:15px 6px 0px;min-width:295px;max-width:97%;padding:4px 0px 2px 10px;min-height:160px;max-height:200px;overflow:auto'
    );
    div.appendChild(list);
    contentEl.appendChild(div);
};
